package com.jytpay.util;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Assert;
import org.xml.sax.InputSource;

import com.jytpay.config.JytPayConfig;


/**
 * 金运通支付客户端
 * @author hyr
 *
 */
public class JytPayClient {
	
	private static final Log logger = LogFactory.getLog(JytPayClient.class);
	
	public static String RESP_MSG_PARAM_SEPARATOR = "&";
	
	/**返回报文merchant_id字段前缀*/
	public static String RESP_MSG_PARAM_PREFIX_MERCHANT_ID = "merchant_id=";
	
	/**返回报文xml_enc字段前缀*/
	public static String RESP_MSG_PARAM_PREFIX_XML_ENC = "xml_enc=";
	/**返回报文xml_enc字段前缀*/
	public static String RESP_MSG_PARAM_PREFIX_KEY_ENC = "key_enc=";
	
	/**返回报文sign字段前缀*/
	public static String RESP_MSG_PARAM_PREFIX_SIGN = "sign=";
	
	private RSAHelper rsaHelper;
	
	public JytPayClient(JytPayConfig config) throws Exception {
		rsaHelper = new RSAHelper();
		rsaHelper.initKey(config.getClientPrivateKey(), config.getServerPublicKey(), 2048);
	}
	
	/**
	 * 获取交易流水号
	 * @param merchantId
	 * @param payNumber
	 * @return
	 */
	public String getTranFlow(String merchantId, String payNumber){
		return merchantId + payNumber;
	}
	
	
	/**
	 * 获得报文头字符串
	 * 
	 * @param tranCode
	 * @param merchantId
	 * @param payNumber
	 * @return
	 */
	public String getMsgHeadXml(String tranCode, String merchantId, String payNumber){
		
		StringBuffer headXml = new StringBuffer();
		headXml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><message><head><version>1.0.0</version>");
		headXml.append("<tran_type>01</tran_type>")
			   .append("<merchant_id>").append(merchantId).append("</merchant_id>");
		headXml.append("<tran_date>").append(DateTimeUtils.getNowDateStr(DateTimeUtils.DATE_FORMAT_YYYYMMDD))
		       .append("</tran_date>");
		headXml.append("<tran_time>").append(DateTimeUtils.getNowDateStr(DateTimeUtils.DATETIME_FORMAT_HHMMSS))
		       .append("</tran_time><tran_flowid>").append(getTranFlow(merchantId, payNumber))
		       .append("</tran_flowid><tran_code>").append(tranCode).append("</tran_code>");
		headXml.append("</head>");
		
		return headXml.toString();
	}
	
	public String sendMsg(String serverUrl, String merchantId, String xml, String sign) throws Exception{
		
		logger.info("上送报文：" + xml);
		logger.info("上送签名：" + sign);

		byte[] des_key = DESHelper.generateDesKey();

		Map<String, String> paramMap = new HashMap<String, String>();
		paramMap.put("merchant_id", merchantId);
		paramMap.put("xml_enc", encryptXml(xml, des_key));
		paramMap.put("key_enc", encryptKey(des_key));
		paramMap.put("sign", sign);

		// 获取执行结果
		String res = HttpClient431Util.doPost(paramMap, serverUrl);

		if (res == null) {
			logger.error("服务器连接失败");
			throw new AppException("测试异常");
		} else {
			logger.info("连接服务器成功,返回结果" + res);
		}
		
		String[] respMsg = res.split(RESP_MSG_PARAM_SEPARATOR);

		//String merchantId = respMsg[0].substring(RESP_MSG_PARAM_PREFIX_MERCHANT_ID.length());
		String respXmlEnc = respMsg[1].substring(RESP_MSG_PARAM_PREFIX_XML_ENC.length());
		String respKeyEnc = respMsg[2].substring(RESP_MSG_PARAM_PREFIX_KEY_ENC.length());
		String respSign = respMsg[3].substring(RESP_MSG_PARAM_PREFIX_SIGN.length());

		byte[] respKey = decryptKey(respKeyEnc);
		String respXml = decrytXml(respXmlEnc, respKey);

		logger.info("返回报文merchantId:" + merchantId);
		logger.info("返回报文XML:" + respXml);
		logger.info("返回报文签名:" + respSign);

		Assert.assertTrue("返回报文校验失败", verifyMsgSign(respXml, respSign));

		return respXml;
	}
	
	public String getMsgRespCode(String respMsg) throws Exception{
        SAXReader saxReader = new SAXReader();
		Document doc = saxReader.read(new InputSource(new StringReader(respMsg)));
		
		//解析报文头
		Node packageHead = doc.selectSingleNode("/message/head");
		
		Node respCodeNode = packageHead.selectSingleNode("resp_code");
		
		return respCodeNode.getText();
	}
	
	public String getMsgState(String respMsg) throws Exception{
        SAXReader saxReader = new SAXReader();
		Document doc = saxReader.read(new InputSource(new StringReader(respMsg)));
		
		//解析报文头
		Node packageHead = doc.selectSingleNode("/message/body");
		if(packageHead==null)
			return null;
		
		Node tranStateNode = packageHead.selectSingleNode("tran_state");
		if(tranStateNode==null)
			return null;
		
		return tranStateNode.getText();
	}
	
	
	public String encryptKey( byte[] key){
		
		byte[] enc_key = null ;
		try {
			enc_key = rsaHelper.encryptRSA(key, false, "UTF-8") ;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return StringUtil.bytesToHexString(enc_key) ;
	}
	
	public byte[] decryptKey(String hexkey){
		byte[] key = null ;
		byte[] enc_key = StringUtil.hexStringToBytes(hexkey) ;
		
		try {
			key = rsaHelper.decryptRSA(enc_key, false, "UTF-8") ;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return key ;
	}
	
	private String encryptXml( String xml, byte[] key){
		
		String enc_xml = CryptoUtils.desEncryptToHex(xml, key) ;
		
		return enc_xml;
	}
	
	public String decrytXml(String xml_enc, byte[] key) {
		String xml = CryptoUtils.desDecryptFromHex(xml_enc, key) ;
		return xml;
	}

	public boolean verifyMsgSign(String xml, String sign)
	{
		byte[] bsign = StringUtil.hexStringToBytes(sign) ;
		
		boolean ret = false ;
		try {
			ret = rsaHelper.verifyRSA(xml.getBytes("UTF-8"), bsign, false, "UTF-8") ;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return ret;
	}
	
	public String signMsg( String xml ){
		String hexSign = null ;
		
		try {
			byte[] sign = rsaHelper.signRSA(xml.getBytes("UTF-8"), false, "UTF-8") ;
			
			hexSign = StringUtil.bytesToHexString(sign) ;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return hexSign;
	}
	
	public boolean verifySign(byte[] plainBytes, byte[] signBytes){
		boolean flag = false;
		try {
			flag = rsaHelper.verifyRSA(plainBytes, signBytes, false, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;
	}

	public String getXmlText(String xml, String path) throws Exception {
		SAXReader saxReader = new SAXReader();
		Document doc = saxReader.read(new InputSource(new StringReader(xml)));
		Node node = doc.selectSingleNode(path);
		return node.getText();
	}
	
}
